package tools.clustering_old;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;

import neobio.alignment.CharSequence;
import neuro.uu.se.utils.Utils;

import tools.aligning.aligning;
import tools.aligning.alignment;
import tools.fasta.fastaParser;
import tools.utils.DoubleMatrix;

public class cluster {
	
	HashMap namesToNumber;
	HashMap numberToNames;
	HashMap seqs;
	ArrayList alignments;
	DoubleMatrix distances;
	int[] clusters;
	/**
	 * @param args
	 */
	public static void main(String[] args)throws Exception {
		// TODO Auto-generated method stub
//		cluster c=new cluster(args[0]);
//		c.align("needle");
		cluster c=new cluster();
		System.out.println("preparing...");
		c.prepareInfile("/local/out/Linn/needle_Allaseq7tm2_VS_Allaseq7tm2.csv","/local/out/Linn/needle_Allaseq7tm2_VS_Allaseq7tm2_parsed.csv");
		System.out.println("generating...");
		c.generateMatrix("/local/out/Linn/needle_Allaseq7tm2_VS_Allaseq7tm2_parsed.csv");
//		c.printNameList("/local/out/Linn/name_list.csv");
//		c.printDistanceMatrix("/local/out/Linn/tmp_matrix.csv");
//		System.out.println("manipulating...");
//		c.distances=MatrixManipulation.euclidianDistance(c.distances);
		System.out.println("clustering...");
		c.kMeanClustering_floating(c.generateSeeds("/local/out/Linn/seeds.txt"),1);
		int nrOfClusters=12;
//		ArrayList separateClusters=new ArrayList();
//		for(int i=0;i<nrOfClusters;i++){
//		ArrayList tmp=new ArrayList();
//		separateClusters.add(tmp);
//		}
//		for (int i = 0; i < c.clusters.length; i++) {
//		((ArrayList)separateClusters.get(c.clusters[i])).add(new Integer(i));
//		}
		int[] mall=c.clusters;
		ArrayList result=new ArrayList();
		for(int i=0;i<10000;i++){
			c.kMeanClustering_floating(c.generateRandomSeeds(nrOfClusters),0.001);
//			result.add(c.adjustResult(mall,c.clusters,nrOfClusters));
			result.add(c.clusters);
		}
		c.createConsensus(result,nrOfClusters);
		result.add(0,mall);
		System.out.println("printing...");
//		c.printClusters("/local/out/Linn/tmp_result_floating.csv");
		c.printMultipleClusters("/local/out/Linn/tmp_many.csv",result);
		
		System.out.println("Done!");
	}
	public int[] adjustResult(int[] mall,int[] result,int nrOfClusters){
		Utils.
		int adjust=1000;
		int[] ans=new int[result.length];
		ArrayList separateClusters=new ArrayList();
		for(int i=0;i<nrOfClusters;i++){
			ArrayList tmp=new ArrayList();
			separateClusters.add(tmp);
		}
		for (int i = 0; i < result.length; i++) {
			((ArrayList)separateClusters.get(result[i])).add(new Integer(i));
		}
		int[] used=new int[separateClusters.size()];
		int[] count;
		int position=0;
		for(int i=0;i<separateClusters.size();i++){
			count=new int[separateClusters.size()];
			//count the overlap with the mall-cluster
			for (Iterator iter = ((ArrayList)separateClusters.get(i)).iterator(); iter.hasNext();) {
				count[mall[((Integer) iter.next()).intValue()]]++;
			}
			//find the highest count
			position=0;
			for(int j=1;j<separateClusters.size();j++){
				if(count[j]>count[position])
					position=j;
			}
			//update the answer array
			for (Iterator iter = ((ArrayList)separateClusters.get(i)).iterator(); iter.hasNext();) {
				ans[((Integer) iter.next()).intValue()]=used[position]*adjust+position;
			}
			used[position]++;
		}
		return ans;
	}
	
	public cluster(){
		
	}
	public cluster(String faFile)throws Exception{
		seqs=new HashMap();
		namesToNumber=new HashMap();
		numberToNames=new HashMap();
		fastaParser fa=new fastaParser(new BufferedReader(new FileReader(faFile)));
		String name="";
		for(int i=0;fa.hasNext();i++){
			name=fa.nextHit().substring(1).split(" ")[0];
			Integer i0=new Integer(i);
			namesToNumber.put(name,i0);
			numberToNames.put(i0,name);
			seqs.put(i0,new CharSequence(new StringReader(fa.getSeq())));
		}
	}
	//needs rework!!!!!!! Do not use!
	public void align(String algorithm)throws Exception{
		alignments=new ArrayList();
		ArrayList threadPool=new ArrayList();
		int size=namesToNumber.keySet().size();
		for(int i=0;i<size-1;i++){
			Thread tmp=new aligning(algorithm);
			//Dangerous stuff with sublists and hashmaps over each other
			((aligning)tmp).setData((String)numberToNames.get(new Integer(i)),(CharSequence)seqs.get(new Integer(i)),new ArrayList(((ArrayList)namesToNumber.keySet()).subList(i+1,size)),new ArrayList(((ArrayList)seqs.entrySet()).subList(i+1,size)));
			threadPool.add(tmp);
		}
		int[] activeThreads=new int[Runtime.getRuntime().availableProcessors()];
		int position=0;
		for (; position < activeThreads.length; position++) {
			activeThreads[position]=position;
			((aligning)threadPool.get(position)).start();
		}
		boolean running;
		ArrayList result;
		while(true){
			running=false;
			for (int i=0;i<activeThreads.length ;i++) {
				aligning element = (aligning) threadPool.get(activeThreads[i]);
				if(!element.isDone()){
					running=true;
				}else{
					result=element.getResult();
					if(result!=null){
//						alignments.addAll(result);
//						printResult(result);
					}
					if(position<threadPool.size()){
						activeThreads[i]=position;
						((aligning)threadPool.get(position)).start();
						position++;
						running=true;
					}
				}
			}
			if(!running)
				break;
			Thread.sleep(10);
		}
	}
	public void generateMatrix()throws Exception{
		distances=new DoubleMatrix(namesToNumber.keySet().size(),namesToNumber.keySet().size());
		int i=0,j=0;
		for (Iterator iter = alignments.iterator(); iter.hasNext();) {
			alignment element = (alignment) iter.next();
			i=((Integer)namesToNumber.get(element.getS1())).intValue();
			j=((Integer)namesToNumber.get(element.getS2())).intValue();
			distances.set(i,j,element.getScore());
			distances.set(j,i,element.getScore());
		}
	}
	public void prepareInfile(String inFile,String outFile) throws Exception{
		BufferedReader in=new BufferedReader(new FileReader(inFile));
		BufferedWriter out=new BufferedWriter(new FileWriter(outFile));
		for(String s=in.readLine();s!=null;s=in.readLine()){
			String[] l=s.split("\t");
//			System.out.println(l.length);
			out.write(l[0]+"\t"+l[1]+"\t"+Double.parseDouble(l[9])/Double.parseDouble(l[5])+"\n");
		}
	}
	/**
	 * Takes an infile as a starting point instead of aligning everything
	 * @param inFile, tab-separated with names in the two first columns and the score in the third
	 * @throws Exception
	 */
	public void generateMatrix(String inFile)throws Exception{
		BufferedReader in=new BufferedReader(new FileReader(inFile));
		namesToNumber=new HashMap();
		numberToNames=new HashMap();
		alignments=new ArrayList();
		int i=0;
		for(String s=in.readLine();s!=null;s=in.readLine()){
			String[] l=s.split("\t");
			alignment tmp=new alignment(l[0],l[1],Double.parseDouble(l[2]));
			if(!namesToNumber.containsKey(tmp.getS1())){
				Integer i0=new Integer(i);
				namesToNumber.put(tmp.getS1(),i0);
				numberToNames.put(i0,tmp.getS1());
				i++;
			}
			if(!namesToNumber.containsKey(tmp.getS2())){
				Integer i0=new Integer(i);
				namesToNumber.put(tmp.getS2(),i0);
				numberToNames.put(i0,tmp.getS2());
				i++;
			}
			alignments.add(tmp);
		}
		generateMatrix();
	}
	public int[] generateSeeds(String inFile)throws Exception{
		ArrayList tmp=new ArrayList();
		BufferedReader in=new BufferedReader(new FileReader(inFile));
		for(String s=in.readLine();s!=null;s=in.readLine()){
			tmp.add(s);
		}
		int[] seeds=new int[tmp.size()];
		for (int i = 0; i < tmp.size(); i++) {
			seeds[i] = ((Integer)namesToNumber.get((String)tmp.get(i))).intValue();
		}
		return seeds;
	}
	public int[] generateRandomSeeds(int antal){
		int[] seeds=new int[antal];
		Random rand=new Random();
		boolean exists;
		for(int i=0;i<antal;){
			seeds[i]=rand.nextInt(namesToNumber.keySet().size());
			exists=false;
			for(int j=0;j<i;j++)
				exists=seeds[i]==seeds[j];
			if(!exists)
				i++;
		}
		return seeds;
	}
	public void kMeanClustering_floating(int[] seeds,double limit)throws Exception{
		clusters=new int[namesToNumber.keySet().size()];
		DoubleMatrix[] curSeeds=new DoubleMatrix[seeds.length];
		for (int i = 0; i < seeds.length; i++) {
			curSeeds[i]=distances.getRow(seeds[i]);
		}
		DoubleMatrix[] nextSeeds=new DoubleMatrix[seeds.length];
		for (int i = 0; i < nextSeeds.length; i++) {
			nextSeeds[i]=new DoubleMatrix(1,distances.getWidth());
		}
		int[] clusterSizes=new int[seeds.length];
		
		double minDistance=9999999,curDistance=0,movement=999999999;
		int curCluster=0;
		while(movement>limit){
			//find out which cluster
			for(int i=0;i<distances.getHeight();i++){
				minDistance=999999;
				for (int j = 0; j < curSeeds.length; j++) {
					curDistance=distances.euclidianLengthFromRowTo(i,curSeeds[j]);
					if(curDistance<minDistance){
						curCluster=j;
						minDistance=curDistance;
					}
				}
//				System.out.println(curCluster);
				clusters[i]=curCluster;
				clusterSizes[curCluster]++;
				nextSeeds[curCluster].addRowToThis(distances,i);
			}
			//finish the calculation of the new seeds and movement
			movement=0;
			for (int i = 0; i < nextSeeds.length; i++) {
				nextSeeds[i].divideThis(clusterSizes[i]);
				clusterSizes[i]=0;
				movement+=curSeeds[i].euclidianLengthFromRowTo(0,nextSeeds[i]);
				curSeeds[i].set(nextSeeds[i]);
				nextSeeds[i].setToZero();
			}
//			System.out.println(movement);
		}
	}
	public void kMeanClustering(int[] seeds,double limit)throws Exception{
		clusters=new int[namesToNumber.keySet().size()];
		ArrayList separateClusters;
		double movement=999999999;
		double minDistance=999999,curDistance=0;
		int clustNr=-1;
		while (movement>limit) {
			separateClusters=new ArrayList();
			for(int i=0;i<seeds.length;i++){
				ArrayList tmp=new ArrayList();
				separateClusters.add(tmp);
			}
			//calculate clusters
			for(int i=0;i<distances.getHeight();i++){
				minDistance=999999;
				for (int j = 0; j < seeds.length; j++) {
					curDistance=distances.get(i,seeds[j]);
					if(curDistance<minDistance){
						clustNr=j;
						minDistance=curDistance;
					}
				}
				clusters[i]=clustNr;
				((ArrayList)separateClusters.get(clustNr)).add(new Integer(i));
			}
			//calculate new seeds
			movement=0;
			for (int clust=0; clust<separateClusters.size();clust++) {
				ArrayList element = (ArrayList) separateClusters.get(clust);
				minDistance=999999999;
				clustNr=0;
				for (Iterator iterator = element.iterator(); iterator.hasNext();) {
					Integer i1 = (Integer) iterator.next();
					curDistance=0;
					for (Iterator iter = element.iterator(); iter.hasNext();) {
						Integer i2 = (Integer) iter.next();
						curDistance+=distances.get(i1.intValue(),i2.intValue());
					}
					if(curDistance<minDistance){
						clustNr=i1.intValue();
						minDistance=curDistance;
					}
				}
				//add the movement and update the seed
				movement+=distances.get(seeds[clust],clustNr);
				seeds[clust]=clustNr;
			}
			System.out.println(movement);
		}
	}
	public void createConsensus(ArrayList result, int nrOfClusters){
		Random rand=new Random();
		int[] consensus=new int[((int[])result.get(0)).length];
		for (int i = 0; i < consensus.length; i++) {
			consensus[i]=((int[])result.get(0))[i];
		}
		double[][] map=new double[((int[])result.get(0)).length][nrOfClusters];
		int[] count,curTry;
		int tint;
		ArrayList position, separateClusters,curCluster;
		for(int i=0;i<result.size();i++){
			curTry=(int[])result.get(i);
			//separate the cluster
			separateClusters=new ArrayList();
			for(int j=0;j<nrOfClusters;j++){
				separateClusters.add(new ArrayList());
			}
			for (int j = 0; j < ((int[])result.get(i)).length; j++) {
				((ArrayList)separateClusters.get(curTry[j])).add(new Integer(j));
			}
			// check most probable consensus cluster for each cluster
			for (int j = 0; j < separateClusters.size(); j++) {
				curCluster=(ArrayList)separateClusters.get(j);
				count=new int[separateClusters.size()];
				//count the overlap with the mall-cluster
//				for(int k=0;k<curCluster.size();k++){
//				
//				}
				for (Iterator iter = curCluster.iterator(); iter.hasNext();) {
					tint=((Integer) iter.next()).intValue();
					count[consensus[tint]]++;
				}
				//find the highest count
				position=new ArrayList();
				position.add(new Integer(0));
				tint=0;
				for(int k=1;k<count.length;k++){
					if(count[k]==count[((Integer)position.get(0)).intValue()]){
						position.add(new Integer(k));
					}else if(count[k]>count[tint]){
						position.removeAll(position);
//						System.out.println(position.size());
						position.add(new Integer(k));
						tint=k;
					}
				}
				//update the map...
				for (Iterator iter = curCluster.iterator(); iter.hasNext();){
					tint=((Integer)iter.next()).intValue();
					for (Iterator iterator = position.iterator(); iterator.hasNext();) {
						map[tint][((Integer)iterator.next()).intValue()]+=1.0/(double)position.size();
					}
				}
			}
//			prints the map matrix
//			System.out.print("0");
//			for (int j = 1; j < map[0].length; j++) {
//				System.out.print("\t"+j);
//			}
//			System.out.println("");
//			for (int j = 0; j < map.length; j++) {
//				System.out.print(map[j][0]);
//				for (int k = 1; k < map[j].length; k++) {
//					System.out.print("\t"+map[j][k]);
//				}
//				System.out.println("");
//			}
			//create a new consensus sequence
			consensus=new int[consensus.length];
			for (int j = 0; j < consensus.length; j++) {
				tint=0;
				for (int k = 1; k < map[j].length; k++) {
					if(map[j][k]==map[j][tint]){
						if(rand.nextBoolean()){
							tint=k;
						}
					}else if(map[j][k]>map[j][tint])
							tint=k;
				}
				consensus[j]=tint;
			}
		}
		result.add(0,consensus);
		double[] score=new double[consensus.length];
		for (int i = 0; i < score.length; i++) {
			score[i]=map[i][consensus[i]];
		}
		result.add(1,score);
	}
//	public void createConsensus(ArrayList result,int nrOfClusters){
//	int[] ans=new int[((int[])result.get(0)).length];
//	int[] antal=new int[((int[])result.get(0)).length];
//	int[] count;
//	int position=0;
//	for (int i = 0; i < ans.length; i++) {
//	count=new int[nrOfClusters];
//	for(int j=0;j<result.size();j++){
//	count[((int[])result.get(j))[i]%1000]++;
//	}
//	for (int j = 0; j < count.length; j++) {
//	if(count[j]>count[position])
//	position=j;
//	}
//	antal[i]=count[position];
//	ans[i]=position;
//	}
//	result.add(0,ans);
//	result.add(1,antal);
//	}
	public void printClusters(String outFile)throws Exception{
		BufferedWriter out=new BufferedWriter(new FileWriter(outFile));
		out.write("Name\tCluster\n");
		for (int i = 0; i < clusters.length; i++) {
			out.write((String)numberToNames.get(new Integer(i))+"\t"+clusters[i]+"\n");
		}
		out.close();
	}
	public void printMultipleClusters(String outFile,ArrayList result)throws Exception{
		BufferedWriter out=new BufferedWriter(new FileWriter(outFile));
		out.write("Name");
		for(int i=0;i<result.size();i++)
			out.write("\tTry "+i);
		for (int j=0;j<numberToNames.keySet().size();j++) {
			String name = (String) numberToNames.get(new Integer(j));
			out.write("\n"+name);
			for(int i=0;i<result.size();i++){
				try{
					out.write("\t"+((int[])result.get(i))[j]);
				}catch(ClassCastException e){
					out.write("\t"+((double[])result.get(i))[j]);
				}
			}
		}
		out.write("\n");
		out.close();
	}
	public void printNameList(String outFile)throws Exception{
		BufferedWriter out=new BufferedWriter(new FileWriter(outFile));
		for (Iterator iter = numberToNames.keySet().iterator(); iter.hasNext();) {
			Integer element = (Integer) iter.next();
			out.write(element.toString()+"\t"+(String)numberToNames.get(element)+"\n");
		}
		out.close();
	}
	public void printDistanceMatrix(String outFile)throws Exception{
		BufferedWriter out=new BufferedWriter(new FileWriter(outFile));
		for(int i=0;i<numberToNames.keySet().size();i++){
			out.write("\t"+(String)numberToNames.get(new Integer(i)));
		}
		for(int i=0;i<numberToNames.keySet().size();i++){
			out.write("\n"+(String)numberToNames.get(new Integer(i)));
			for(int j=0;j<numberToNames.keySet().size();j++){
				out.write("\t"+distances.get(i,j));
			}
		}
		out.write("\n");
		out.close();
	}
}
